﻿using System.Linq;
using CID.Domain.Exceptions;
using CID.Domain.Infrastructure;
using CID.Tests.Support;
using NUnit.Framework;
using Rhino.Mocks;
using System.Collections.Generic;

namespace CID.Domain.Tests.Infrastructure
{
    [TestFixture]
    class DefinitionTests : BaseFunctionalTests
    {
        #region Defintion Implementation Class
        private class Definition : Domain.Infrastructure.Definition
        {
            private bool checkValidParameterForTemplateWasCalled = false;
            
            public bool CheckValidParameterForTemplateWasCalled { get { return checkValidParameterForTemplateWasCalled; } }
            
            protected override void CheckValidParameterForTemplate(Domain.Infrastructure.IParameter parameter)
            {
                checkValidParameterForTemplateWasCalled = true;
            }

            public void ResetCheckValidParameterForTemplateWasCalled()
            {
                checkValidParameterForTemplateWasCalled = false;
            }

            public IDefaultParameterValue DoAddDefaultParameterValue(IParameter parameter) {
                return this.AddDefaultParameterValue(parameter);
            }

            public void DoRemoveDefaultParameterValue(IParameter parameter) {
                this.RemoveDefaultParameterValue(parameter);
            }

            public void DoCleanParameterValuesFor(ITemplate template)
            {
                CleanParameterValuesFor(template);
            }

            public void DoCreateDefaultParameterValuesFor(ITemplate template)
            {
                CreateDefaultParameterValuesFor(template);
            }
        }
        #endregion

        private Definition definition;

        public override void SetUp()
        {
            base.SetUp();
            definition = new Definition();
        }

        #region AddDefaultParameterValue
        [Test]
        public void AddDefaultParameterValue_AddItOnce_ShouldSucceed()
        { 
            var parameter = StubsBuilder.BuildParameterStub();
            
            var parameterValue = definition.DoAddDefaultParameterValue(parameter);

            Assert.IsInstanceOf<DefaultParameterValue>(parameterValue);
            Assert.AreEqual(definition, parameterValue.Definition);
            Assert.AreEqual(parameter, parameterValue.Parameter);
            Assert.AreEqual(1, definition.ParameterValues.Count());
            Assert.AreEqual(1, definition.DefaultParameterValues.Count());
            CollectionAssert.IsEmpty(definition.EnvironmentParameterValues);
            CollectionAssert.Contains(definition.ParameterValues, parameterValue);
            CollectionAssert.Contains(definition.DefaultParameterValues, parameterValue);
            Assert.IsTrue(definition.CheckValidParameterForTemplateWasCalled);
        }

        [Test, ExpectedException(typeof(DuplicateParameterValueDefinitionException))]
        public void AddDefaultParameterValue_AddItTwice_ShouldThrowException()
        {
            var parameter = StubsBuilder.BuildParameterStub();

            definition.DoAddDefaultParameterValue(parameter);
            definition.DoAddDefaultParameterValue(parameter);
        }
        #endregion

        #region RemoveDefaultParameterValue
        [Test]
        public void RemoveDefaultParameterValueThroughParameter_ParameterValuePreviouslyAdded_ShouldSuccceed()
        {
            var parameter1 = StubsBuilder.BuildParameterStub();
            var parameter1Value = definition.DoAddDefaultParameterValue(parameter1);
            var parameter2 = StubsBuilder.BuildParameterStub();
            var parameter2Value = definition.DoAddDefaultParameterValue(parameter2);
            definition.ResetCheckValidParameterForTemplateWasCalled();

            definition.DoRemoveDefaultParameterValue(parameter1);

            CollectionAssert.AreEquivalent(new IParameterValue[] { parameter2Value }, definition.ParameterValues);
            CollectionAssert.AreEquivalent(new IDefaultParameterValue[] { parameter2Value }, definition.DefaultParameterValues);
            CollectionAssert.DoesNotContain(definition.ParameterValues, parameter1Value);
            Assert.IsTrue(definition.CheckValidParameterForTemplateWasCalled);
        }

        [Test]
        public void RemoveDefaultParameterValueThroughParameter_ParameterValueNotAdded_NothingShouldHappen()
        {
            var parameter1 = StubsBuilder.BuildParameterStub();
            var parameter1Value = definition.DoAddDefaultParameterValue(parameter1);
            var parameter2 = StubsBuilder.BuildParameterStub();
            var parameter2Value = definition.DoAddDefaultParameterValue(parameter2);
            var parameter3 = StubsBuilder.BuildParameterStub();
            definition.ResetCheckValidParameterForTemplateWasCalled();

            definition.DoRemoveDefaultParameterValue(parameter3);

            CollectionAssert.AreEquivalent(new IParameterValue[] { parameter1Value, parameter2Value }, definition.ParameterValues);
            CollectionAssert.AreEquivalent(new IDefaultParameterValue[] { parameter1Value, parameter2Value }, definition.DefaultParameterValues);
            Assert.IsTrue(definition.CheckValidParameterForTemplateWasCalled);
        }

        #endregion

        #region AddEnvironmentParameterValue
        [Test]
        public void AddEnvironmentParameterValue_AddItOnce_ShouldSucceed()
        {
            var parameter = StubsBuilder.BuildParameterStub();
            var environment = Operations.StubsBuilder.BuildEnvironmentStub();

            var parameterValue = definition.AddEnvironmentParameterValue(parameter, environment);

            Assert.IsInstanceOf<EnvironmentParameterValue>(parameterValue);
            Assert.AreEqual(definition, parameterValue.Definition);
            Assert.AreEqual(parameter, parameterValue.Parameter);
            Assert.AreEqual(environment, parameterValue.Environment);
            Assert.AreEqual(1, definition.ParameterValues.Count());
            Assert.AreEqual(1, definition.EnvironmentParameterValues.Count());
            CollectionAssert.IsEmpty(definition.DefaultParameterValues);
            CollectionAssert.Contains(definition.ParameterValues, parameterValue);
            CollectionAssert.Contains(definition.EnvironmentParameterValues, parameterValue);
            Assert.IsTrue(definition.CheckValidParameterForTemplateWasCalled);
        }

        [Test, ExpectedException(typeof(DuplicateParameterValueDefinitionException))]
        public void AddEnvironmentParameterValue_AddItTwice_ShouldThrowException()
        {
            var parameter = StubsBuilder.BuildParameterStub();
            var environment = Operations.StubsBuilder.BuildEnvironmentStub();

            definition.AddEnvironmentParameterValue(parameter, environment);
            definition.AddEnvironmentParameterValue(parameter, environment);
        }
        #endregion

        #region RemoveEnvironmentParameterValue
        [Test]
        public void RemoveEnvironmentParameterValueThroughParameterAndEnvironment_ParameterValuePreviouslyAdded_ShouldSuccceed()
        {
            var parameter1 = StubsBuilder.BuildParameterStub();
            var environment1 = Operations.StubsBuilder.BuildEnvironmentStub();
            var parameter1Value = definition.AddEnvironmentParameterValue(parameter1, environment1);
            var parameter2 = StubsBuilder.BuildParameterStub();
            var environment2 = Operations.StubsBuilder.BuildEnvironmentStub();
            var parameter2Value = definition.AddEnvironmentParameterValue(parameter2, environment2);
            definition.ResetCheckValidParameterForTemplateWasCalled();

            definition.RemoveEnvironmentParameterValue(parameter1, environment1);

            CollectionAssert.AreEquivalent(new IParameterValue[] { parameter2Value }, definition.ParameterValues);
            CollectionAssert.AreEquivalent(new IEnvironmentParameterValue[] { parameter2Value }, definition.EnvironmentParameterValues);
            CollectionAssert.DoesNotContain(definition.ParameterValues, parameter1Value);
            Assert.IsTrue(definition.CheckValidParameterForTemplateWasCalled);
        }

        [Test]
        public void RemoveEnvironmentParameterValueThroughParameterAndEnvironment_ParameterValueNotAdded_NothingShouldHappen()
        {
            var parameter1 = StubsBuilder.BuildParameterStub();
            var environment1 = Operations.StubsBuilder.BuildEnvironmentStub();
            var parameter1Value = definition.AddEnvironmentParameterValue(parameter1, environment1);
            var parameter2 = StubsBuilder.BuildParameterStub();
            var environment2 = Operations.StubsBuilder.BuildEnvironmentStub();
            var parameter2Value = definition.AddEnvironmentParameterValue(parameter2, environment2);
            var parameter3 = StubsBuilder.BuildParameterStub();
            var environment3 = Operations.StubsBuilder.BuildEnvironmentStub();
            definition.ResetCheckValidParameterForTemplateWasCalled();

            definition.RemoveEnvironmentParameterValue(parameter3, environment3);

            CollectionAssert.AreEquivalent(new IParameterValue[] { parameter1Value, parameter2Value }, definition.ParameterValues);
            CollectionAssert.AreEquivalent(new IEnvironmentParameterValue[] { parameter1Value, parameter2Value }, definition.EnvironmentParameterValues);
            Assert.IsTrue(definition.CheckValidParameterForTemplateWasCalled);
        }

        [Test]
        public void RemoveEnvironmentParameterValueThroughParameterValue_ParameterValuePreviouslyAdded_ShouldSuccceed()
        {
            var parameter1 = StubsBuilder.BuildParameterStub();
            var environment1 = Operations.StubsBuilder.BuildEnvironmentStub();
            var parameter1Value = definition.AddEnvironmentParameterValue(parameter1, environment1);
            var parameter2 = StubsBuilder.BuildParameterStub();
            var environment2 = Operations.StubsBuilder.BuildEnvironmentStub();
            var parameter2Value = definition.AddEnvironmentParameterValue(parameter2, environment2);
            definition.ResetCheckValidParameterForTemplateWasCalled();

            definition.RemoveEnvironmentParameterValue(parameter1Value);

            CollectionAssert.AreEquivalent(new IParameterValue[] { parameter2Value }, definition.ParameterValues);
            CollectionAssert.AreEquivalent(new IEnvironmentParameterValue[] { parameter2Value }, definition.EnvironmentParameterValues);
            CollectionAssert.DoesNotContain(definition.ParameterValues, parameter1Value);
            Assert.IsFalse(definition.CheckValidParameterForTemplateWasCalled);
        }

        [Test]
        public void RemoveEnvironmentParameterValueThroughParameterValue_ParameterValueNotAdded_NothingShouldHappen()
        {
            var parameter1 = StubsBuilder.BuildParameterStub();
            var environment1 = Operations.StubsBuilder.BuildEnvironmentStub();
            var parameter1Value = definition.AddEnvironmentParameterValue(parameter1, environment1);
            var parameter2 = StubsBuilder.BuildParameterStub();
            var environment2 = Operations.StubsBuilder.BuildEnvironmentStub();
            var parameter2Value = definition.AddEnvironmentParameterValue(parameter2, environment2);
            var parameter3 = StubsBuilder.BuildParameterStub();
            var environment3 = Operations.StubsBuilder.BuildEnvironmentStub();
            var parameter3Value = StubsBuilder.BuildEnvironmentParameterValueStub(environment3, parameter3, definition);
            definition.ResetCheckValidParameterForTemplateWasCalled();

            definition.RemoveEnvironmentParameterValue(parameter3Value);

            CollectionAssert.AreEquivalent(new IParameterValue[] { parameter1Value, parameter2Value }, definition.ParameterValues);
            CollectionAssert.AreEquivalent(new IEnvironmentParameterValue[] { parameter1Value, parameter2Value }, definition.EnvironmentParameterValues);
            Assert.IsFalse(definition.CheckValidParameterForTemplateWasCalled);
        }
        #endregion

        #region Automatic Parameter Handling
        [Test]
        public void CleanParameterValuesFor_ParametersValuesForSpecifiedTemplateShouldBeRemoved()
        {
            var template1 = StubsBuilder.BuildTemplateStubWithAllParameterTypes();
            template1.Stub(x => x.Id).Return(1);
            var template2 = StubsBuilder.BuildTemplateStubWithAllParameterTypes();
            template2.Stub(x => x.Id).Return(2);
            var environment = Operations.StubsBuilder.BuildEnvironmentStub();
            foreach (var parameter in template1.Parameters)
            {
                definition.DoAddDefaultParameterValue(parameter);
                definition.AddEnvironmentParameterValue(parameter, environment);
            }
            foreach (var parameter in template2.Parameters)
            {
                definition.DoAddDefaultParameterValue(parameter);
                definition.AddEnvironmentParameterValue(parameter, environment);
            }

            definition.DoCleanParameterValuesFor(template1);

            Assert.AreEqual(template2.Parameters.Count() * 2, definition.ParameterValues.Count());
            foreach (var parameter in template1.Parameters)
            {
                Assert.IsNull(definition.GetDefaultParameterValue(parameter));
                Assert.IsNull(definition.GetEnvironmentParameterValue(parameter, environment));
            }
            foreach (var parameter in template2.Parameters)
            {
                Assert.IsNotNull(definition.GetDefaultParameterValue(parameter));
                Assert.IsNotNull(definition.GetEnvironmentParameterValue(parameter, environment));
            }
        }

        [Test]
        public void CreateDefaultParameterValuesFor_ShouldCreateDefautParametersForSpecifiedTemplate()
        {
            var template = StubsBuilder.BuildTemplateStubWithAllParameterTypes();

            definition.DoCreateDefaultParameterValuesFor(template);
            Assert.AreEqual(template.Parameters.Count(), definition.ParameterValues.Count());
            foreach (var parameter in template.Parameters)
                Assert.IsNotNull(definition.GetDefaultParameterValue(parameter));
        }
        #endregion
    }
}
